//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

/**
 * Linux共享内存是通过tmpfs这个文件系统来实现的，tmpfs文件系的目录为/dev/shm，/dev/shm是
 * 驻留在内存 RAM 当中的，因此读写速度与读写内存速度一样，/dev/shm的容量默认尺寸为系统内存
 * 大小的一半大小，使用df -h命令可以看到。但实际上它并不会真正的占用这块内存，如果/dev/shm/
 * 下没有任何文件，它占用的内存实际上就是0字节，仅在使用shm_open文件时，/dev/shm才会真正占
 * 用内存。
 */

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <iostream>

/**
 * 要打开或创建的共享内存文件名，由于shm_open　打开或操作的文件都是位于/dev/shm目录的，因此
 * name不能带路径，例如：/var/myshare 这样的名称是错误的，而 myshare 是正确的，因为
 * myshare 不带任何路径。如果你一定要在name添加路径，那么，请在/dev/shm目录里创建一个目录，
 * 例如，如果你想创建一个  bill/myshare 的共享内存文件，那么请先在/dev/shm目录里创建
 * bill这个子目录，由于不同厂家发布的linux系统的tmpfs的位置也许不是/dev/shm，因此带路径的
 * 名称也许在别的环境下打开不成功。
 */
#define SHM_NAME "como_fscp_named_shm"

void *ShmInit(int& shm_fd, const int shm_size, const bool isServer)
{
    if (isServer) {
        shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    }
    else {
        shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    }

    if (-1 == shm_fd) {
        return nullptr;
    }

    if (ftruncate(shm_fd, shm_size) == -1) {
        close(shm_fd);
        return nullptr;
    }

    void *ptr = mmap(nullptr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (MAP_FAILED == ptr) {
        close(shm_fd);
        return nullptr;
    }


    return ptr;
}

int ShmClose(int shm_fd, void *ptr, int shm_size)
{
    munmap(ptr, shm_size);
    close(shm_fd);

    return 0;
}

/**
 * 删除/dev/shm目录的文件，shm_unlink 删除的文件是由shm_open函数创建于 /dev/shm 目录的。
 * 可以用系统函数unlink来达到同样的效果，用 /dev/shm + name 组成完整的路径即可，但一般不要
 * 这么做，因为系统的 tmpfs 的位置也许不是 /dev/shm。用 shm_open 创建的文件，如果不调用此
 * 函数删除，会一直存在于 /dev/shm 目录里，直到操作系统重启或者调用linux命令rm来删除为止。
 */
int ShmUnlink()
{
    return shm_unlink(SHM_NAME);
}

/**
 * g++ pmalloc/src/sharemem_utils.cpp -lrt
 */
#define SHM_SIZE 1024
int main(int argc, char *argv[])
{
    int shm_fd;
    if (argc < 2) {
        void *ptr = ShmInit(shm_fd, SHM_SIZE, true);
        strcpy(static_cast<char*>(ptr), "Hello from Server!");
        std::cout << "Server wrote to named shared memory." << std::endl;
        ShmClose(shm_fd, ptr, SHM_SIZE);
    }
    else {
        void *ptr = ShmInit(shm_fd, SHM_SIZE, false);
        std::cout << "Client read from named shared memory: " << static_cast<char*>(ptr) << std::endl;
        ShmClose(shm_fd, ptr, SHM_SIZE);
    }
}
